#ifndef HSCENE_H_INCLUDED
#define HSCENE_H_INCLUDED

/* NewScene */
int NewScene(lua_State* l) {
	uint n = lua_gettop(l);
	bool activateScene = false;
	if(n == 1 && isbool(1)) activateScene = tobool(1);

	P.Scenes.push_back(P.Scenes[0]->createNewSceneManager(false));

	auto id = P.Scenes.size()-1;

	array<uint> SceneNode;
	//P.SceneNodes.push_back(SceneNode);

	P.MetaSelectors.push_back(P.Scenes[id]->createMetaTriangleSelector());

	if(activateScene) {
		P.ActiveScene = id;
		if (DEBUG)
			printf("Switch to scene %d\n", id);
	}

	lua_pushnumber(l, activateScene?id:P.Scenes.size()-1);

	return 1;
}

/* ActivateScene */
int ActivateScene(lua_State* l) {
	if (lua_gettop(l) && isnum(1)) {
		uint toActivate = tonum(1);
		if (toActivate >= 0 && toActivate < P.Scenes.size()) {
			P.ActiveScene = toActivate;

			if (DEBUG)
				printf("Switch to scene %d\n", toActivate);

			lua_pop(l, 1);

			return 0;
		}
	}

	return -1;
}

/* ClearScene(uint scene_id) */
int ClearScene(lua_State* l) {
	if (lua_gettop(l) && isnum(1)) {
		uint id = tonum(1);
		if (id >= 0 && id < P.Scenes.size()) {
			/*uint cnt = 0;
			for (uint i=0; i < P.SceneNodes[P.ActiveScene].size(); i++) {
				uint id = P.SceneNodes[P.ActiveScene][i];
				if (id >= 0 && id < P.Nodes.size()) {
					cnt++;
					P.Nodes[id] = nullptr;
				}
			}*/
			P.Tmgr.clear();
			P.MetaSelectors[id]->removeAllTriangleSelectors();
			P.Scenes[id]->clear();
			printf("ClearScene: scene %d cleared\n", id);
		} else
			return LOG_ERROR("ClearScene: invalid id - scene!");
	}
	return 0;
}

/* AddNode(int parent, string mesh, string material) */
int AddNode(lua_State* l) {
	uint n = lua_gettop(l);
	if (!n) { lua_pop(l, 1); return -1; }
	stringc model, material;
	int parent = 0;
	bool physical = true;
	if (n >= 1 && isnum(1)) parent = tonum(1);
	if (n >= 2 && isstr(2)) model = tostr(2);
	if (n >= 3 && isstr(3)) material = tostr(3);
	if (n == 4 && isbool(4)) physical = tobool(4);

	IAnimatedMesh* mesh = P.Scene()->getMesh(model);
	if (!mesh) { lua_pop(l, 1); return LOG_ERROR("Cant create mesh"); }

	IAnimatedMeshSceneNode* node = P.Scene()->addAnimatedMeshSceneNode(mesh, parent?P.Nodes[parent]:0, P.Nodes.size());
	if (node) {
		if (material.size() > 0)
			node->setMaterialTexture( 0, P.Driver->getTexture(material));

		node->getMaterial(0).SpecularColor.set(0,0,0,0);

		//node->setMaterialFlag(EMF_LIGHTING, false);

		if (physical) {
			ITriangleSelector *selector = P.Scene()->createOctreeTriangleSelector(mesh, node);
			P.MetaSelectors[P.ActiveScene]->addTriangleSelector(selector);
			node->setTriangleSelector(selector);
			selector->drop();
		}
	} else {
		lua_pop(l, n);
		return -3;
	}

	int id = P.Nodes.size();

	P.Nodes.push_back(node);

	printf("Node created on scene %d: %d\n", P.ActiveScene, id);

	lua_pushnumber(l, id);

	return 1;
}

/* GetPos(uint node_id) */
int GetPos(lua_State *l) {
	if (!lua_gettop(l)) return LOG_ERROR("GetPos: no parameter found - node id!");
	if (!isnum(1)) return LOG_ERROR("GetPos: first parametr must be number - node id!");
	uint id = tonum(1);

	if (id >= P.Nodes.size()) return LOG_ERROR("GetPos: invalid node id!");

	lua_newtable(l);
	lua_pushnumber(l, P.Nodes[id]->getPosition().X);
	lua_rawseti(l, -2, 1);
	lua_pushnumber(l, P.Nodes[id]->getPosition().Y);
	lua_rawseti(l, -2, 2);
	lua_pushnumber(l, P.Nodes[id]->getPosition().Z);
	lua_rawseti(l, -2, 3);

	return 1;
}

/* 	MoveNode(uint id, bool relative, num x, num y, num z)
	MoveNode(uint id, bool relative, { num x, num y, num z }) */
int MoveNode(lua_State* l) {
	uint n = lua_gettop(l), id;
	vector3df newPosition;
	bool relative = false;
	if (n >= 1 && isnum(1)) id = tonum(1);
	if (!P.Nodes[id]) return LOG_ERROR("MoveNode: invalid id - node!");
	if (n >= 2 && isbool(2)) relative = tobool(2);
	if (n >= 3 && isnum(3)) newPosition.X = tonum(3);
	if (n >= 4 && isnum(4)) newPosition.Y = tonum(4);
	if (n == 5 && isnum(5)) newPosition.Z = tonum(5);

	if (n == 3 && lua_istable(l, 3)) {
        lua_pushnil(l);

        lua_next(l,3);
        newPosition.X = tonum(5);
        lua_pop(l, 1);

        lua_next(l,3);
		newPosition.Y = tonum(5);
		lua_pop(l, 1);

		lua_next(l,3);
		newPosition.Z = tonum(5);
		lua_pop(l, 1);
	}

	if (id >= 0 && id < P.Nodes.size()) {
		P.Nodes[id]->setPosition((relative?P.Nodes[id]->getPosition():vector3df(0)) + newPosition);
		printf("Move node %d on scene %d to %f/%f/%f\n", id, P.ActiveScene, newPosition.X, newPosition.Y, newPosition.Z);
	}

	return 0;
}

/* 	RotateNode(uint id, bool relative, num x, num y, num z)
	RotateNode(uint id, bool relative, { num x, num y, num z }) */
int RotateNode(lua_State* l) {
	uint n = lua_gettop(l), id;
	vector3df newRotate;
	bool relative = false;
	if (n >= 1 && isnum(1)) id = tonum(1);
	if (!P.Nodes[id]) return LOG_ERROR("RotateNode: invalid id - node!");

	if (n >= 2 && isbool(2)) relative = tobool(2);
	if (n >= 3 && isnum(3)) newRotate.X = tonum(3);
	if (n >= 4 && isnum(4)) newRotate.Y = tonum(4);
	if (n == 5 && isnum(5)) newRotate.Z = tonum(5);

	if (n == 3 && lua_istable(l, 3)) {
        lua_pushnil(l);

        lua_next(l,3);
        newRotate.X = tonum(5);
        lua_pop(l, 1);

        lua_next(l,3);
		newRotate.Y = tonum(5);
		lua_pop(l, 1);

		lua_next(l,3);
		newRotate.Z = tonum(5);
		lua_pop(l, 1);
	}

	if (id >= 0 && id < P.Nodes.size()) {
		P.Nodes[id]->setRotation((relative?P.Nodes[id]->getRotation():vector3df(0,0,0)) + newRotate);
		printf("Rotate node %d to %f/%f/%f\n", id, newRotate.X, newRotate.Y, newRotate.Z);
	}

	return 0;
}

/* RemoveNode(uint id) */
int RemoveNode(lua_State* l) {
	uint n = lua_gettop(l);
	if (!n || !isnum(1)) return -1;
	uint id = tonum(1);

	if(id < 0 || id >= P.Nodes.size()) return -1;
	printf("Trying delete node: %d\n", id);
	P.Device->getSceneManager()->addToDeletionQueue(P.Nodes[id]);
	P.Nodes[id] = nullptr;

	return 0;
}

/* AddCameraFPS(float moveSpeed, float rotateSpeed, bool WASD) */
int AddCameraFPS(lua_State* l) {
	uint n = lua_gettop(l);
	float moveSpeed, rotateSpeed;
	bool WASD = false;
	if (n >= 1 && isnum(1)) moveSpeed = tonum(1);
	if (n >= 2 && isnum(2)) rotateSpeed = tonum(2);
	if (n == 3 && isbool(3)) WASD = tobool(3);

	SKeyMap keymap[4];
	if (WASD) {
		keymap[0].KeyCode = KEY_KEY_W;
		keymap[0].Action = EKA_MOVE_FORWARD;
		keymap[1].KeyCode = KEY_KEY_S;
		keymap[1].Action = EKA_MOVE_BACKWARD;
		keymap[2].KeyCode = KEY_KEY_A;
		keymap[2].Action = EKA_STRAFE_LEFT;
		keymap[3].KeyCode = KEY_KEY_D;
		keymap[3].Action = EKA_STRAFE_RIGHT;
	}
	ICameraSceneNode* camera = P.Scene()->addCameraSceneNodeFPS(0, moveSpeed, rotateSpeed, -1, WASD?keymap:0, WASD?4:0);
	P.Scene()->setActiveCamera(camera);

	ISceneNodeAnimator* anim = P.Scene()->createCollisionResponseAnimator(P.MetaSelectors[P.ActiveScene], camera, vector3df(25,50,25), vector3df(0,-5,0), vector3df(0,0,0));
	camera->addAnimator(anim);
	anim->drop();

	P.Dummy = P.Scene()->addCubeSceneNode(15, camera);
	P.Dummy->setScale(vector3df(1,4,1));

	uint id = P.Nodes.size();
	P.Nodes.push_back(P.Dummy);

	lua_pushnumber(l, id);

	return 1;
}

int MakeNormalTexture(lua_State* l) {
	uint n = lua_gettop(l);
	if (!n) return LOG_ERROR("MakeNormalTexture: no parameters found!");
	uint id;
	const char* texture;
	float hardness = 3.0f;

	if (n >= 1 && isnum(1)) id = tonum(1);
	if (n >= 2 && isstr(2)) texture = tostr(2);
	if (n >= 3 && isnum(3)) hardness = tonum(3);

	if (id <= 0 && id > P.Nodes.size()) return LOG_ERROR("MakeNormalTexture: invalid id!");
	ISceneNode* node = P.Nodes[id];

	ITexture* normalMap = P.Driver->getTexture(texture);
	if (normalMap) P.Driver->makeNormalMapTexture(normalMap, hardness);

	node->setMaterialTexture(1, normalMap);

	node->setMaterialType(EMT_NORMAL_MAP_SOLID);
	// adjust height for parallax effect
	node->getMaterial(0).MaterialTypeParam = 1.f / 64.f;

	return 0;
}

int AddLight(lua_State* l) {
	uint n = lua_gettop(l);
	if (!n) return LOG_ERROR("AddLight: no parameters found!");
	uint parent, id;
	float radius;
	vector3df position;
	SColorf color;
	if (n >= 1 && isnum(1)) parent = tonum(1);
	if (n >= 2 && isnum(2)) radius = tonum(2);
	if (n >= 3 && lua_istable(l, 3)) {
		lua_pushnil(l);

        lua_next(l,3);
        position.X = tonum(n+2);
        lua_pop(l, 1);

        lua_next(l,3);
		position.Y = tonum(n+2);
		lua_pop(l, 1);

		lua_next(l,3);
		position.Z = tonum(n+2);
		lua_pop(l, 1);
	}
	if (n >= 4 && lua_istable(l, 4)) {
		lua_pushnil(l);

        lua_next(l,4);
        color.r = tonum(n+1);
        lua_pop(l, 1);

        lua_next(l,4);
		color.g = tonum(n+1);
		lua_pop(l, 1);

		lua_next(l,4);
		color.b = tonum(n+1);
		lua_pop(l, 1);

		lua_next(l,4);
		color.a = tonum(n+1);
		lua_pop(l, 1);
	}

	if (parent < 0 || parent >=P.Nodes.size()) parent = 0;

		// add light 2 (red)
    ISceneNode* light = P.Scene()->addLightSceneNode(parent==0?0:P.Nodes[parent], position, color, radius);

    printf("Created light in %7.1f/%7.1f/%7.1f with color %7.1f/%7.1f/%7.1f/%7.1f and radius %7.1f\n", position.X, position.Y, position.Z, color.r, color.g, color.b, color.a, radius);

    id = P.Nodes.size();
    P.Nodes.push_back(light);

    lua_pushnumber(l, id);

	return 1;

}

int AddAnimator(lua_State* l) {
	uint n = lua_gettop(l);
	if (!n) return LOG_ERROR("AddAnimator: no parameters found!");
	uint type = 0, nodeId;

	if (n >= 1 && isnum(1)) nodeId = tonum(1);
	if (n >= 2 && isnum(2)) type = tonum(2);

	if (nodeId < 0 || nodeId >= P.Nodes.size() || !P.Nodes[nodeId]) return LOG_ERROR("AddAnimator: invalid node id!");

	switch (type) {
		case AT_FLYSTRAIGHT: {
			uint timeForWay;
			vector3df startPoint, endPoint;
			bool loop = false, pingpong = false;

			if (n >= 3 && lua_istable(l, 3)) { //start point
				lua_pushnil(l);

				lua_next(l,3);
				startPoint.X = tonum(n+2);
				lua_pop(l, 1);

				lua_next(l,3);
				startPoint.Y = tonum(n+2);
				lua_pop(l, 1);

				lua_next(l,3);
				startPoint.Z = tonum(n+2);
				lua_pop(l, 1);
			}
			if (n >= 4 && lua_istable(l, 4)) { //end point
				lua_pushnil(l);

				lua_next(l,4);
				endPoint.X = tonum(n+3);
				lua_pop(l, 1);

				lua_next(l,4);
				endPoint.Y = tonum(n+3);
				lua_pop(l, 1);

				lua_next(l,4);
				endPoint.Z = tonum(n+3);
				lua_pop(l, 1);
			}
			if (n >= 5 && isnum(5)) timeForWay = tonum(5);
			if (n >= 6 && isbool(6)) loop = tobool(6);
			if (n == 7 && isbool(7)) pingpong = tobool(7);

			ISceneNodeAnimator* anim = P.Scene()->createFlyStraightAnimator(startPoint, endPoint, timeForWay, loop, pingpong);
			P.Nodes[nodeId]->addAnimator(anim);
			anim->drop();

			if (DEBUG)
				printf("Created fly straight animator to node %d from %7.1f/%7.1f/%7.1f to %7.1f/%7.1f/%7.1f for %dms, %s, %s\n",nodeId,startPoint.X,startPoint.Y,startPoint.Z,endPoint.X,endPoint.Y,endPoint.Z,timeForWay,loop?"looped":"not looped",pingpong?"ping ponged":"not ping ponged");

			lua_pop(l, 10);

			break;
		}
		case AT_FLYCIRCLE: {
            vector3df point, direction = vector3df(0,1,0);
            float radius = 0, radiusEllipsoid = 0, startPosition = 0, speed = 10;

            if (n >= 3 && lua_istable(l, 3)) { //point
				lua_pushnil(l);

				lua_next(l,3);
				point.X = tonum(n+2);
				lua_pop(l, 1);

				lua_next(l,3);
				point.Y = tonum(n+2);
				lua_pop(l, 1);

				lua_next(l,3);
				point.Z = tonum(n+2);
				lua_pop(l, 1);
			}

			if (n >= 4 && isnum(4)) radius = tonum(4);
			if (n >= 5 && isnum(5)) speed = tonum(5);

			if (n >= 6 && lua_istable(l, 6)) { //direction
				lua_pushnil(l);

				lua_next(l,6);
				direction.X = tonum(n+1);
				lua_pop(l, 1);

				lua_next(l,6);
				direction.Y = tonum(n+1);
				lua_pop(l, 1);

				lua_next(l,6);
				direction.Z = tonum(n+1);
				lua_pop(l, 1);
			}

			if (n >= 7 && isnum(7)) startPosition = tonum(7);
			if (n >= 8 && isnum(8)) radiusEllipsoid = tonum(8);

			ISceneNodeAnimator* anim = P.Scene()->createFlyCircleAnimator(point,radius,speed,direction,startPosition,radiusEllipsoid);
			P.Nodes[nodeId]->addAnimator(anim);
			anim->drop();

			if (DEBUG)
				printf("Created fly circle animator around %7.1f/%7.1f/%7.1f with raduis %7.1f speed %7.1f direction %7.1f/%7.1f/%7.1f, start from %7.1f, elipsoid radius %7.1f\n",point.X,point.Y,point.Z,radius,speed,direction.X,direction.Y,direction.Z,startPosition,radiusEllipsoid);

			break;
		}
		case AT_ROTATOR: {
			vector3df direction = vector3df(0,1,0);
			if (n >= 3 && lua_istable(l, 3)) { //point
				lua_pushnil(l);

				lua_next(l,3);
				direction.X = tonum(5);
				lua_pop(l, 1);

				lua_next(l,3);
				direction.Y = tonum(5);
				lua_pop(l, 1);

				lua_next(l,3);
				direction.Z = tonum(5);
				lua_pop(l, 1);

				ISceneNodeAnimator* anim = P.Scene()->createRotationAnimator(vector3df(0,1,0));
				P.Nodes[nodeId]->addAnimator(anim);
				anim->drop();

				if (DEBUG)
					printf("Created rotator animator to node %d with direction %7.1f/%7.1f/%7.1f\n", nodeId, direction.X, direction.Y, direction.Z);

				lua_pop(l, 5);
			}
			break;
		}
		default:
			return LOG_ERROR("AddAnimator: invalid type!");
	}
	return 0;
}

/* MoveCamera(uint x, uint y, uint z) */
int CameraPos(lua_State* l) {
	uint n = lua_gettop(l);
	vector3df newPosition;
	if (n >= 1 && isnum(1)) newPosition.X = tonum(1);
	if (n >= 2 && isnum(2)) newPosition.Y = tonum(2);
	if (n >= 3 && isnum(3)) newPosition.Z = tonum(3);
	P.Scene()->getActiveCamera()->setPosition(newPosition);

	if (DEBUG)
		printf("Set camera position to %f/%f/%f\n", newPosition.X, newPosition.Y, newPosition.Z);

	return 0;
}

/* GetPos(uint node_id) */
int GetCameraPos(lua_State *l) {
	lua_newtable(l);
	lua_pushnumber(l, P.Scene()->getActiveCamera()->getPosition().X);
	lua_rawseti(l, -2, 1);
	lua_pushnumber(l, P.Scene()->getActiveCamera()->getPosition().Y);
	lua_rawseti(l, -2, 2);
	lua_pushnumber(l, P.Scene()->getActiveCamera()->getPosition().Z);
	lua_rawseti(l, -2, 3);

	return 1;
}

void initSceneFuncs() {
	REG(AddNode);
   	REG(RemoveNode);
   	REG(AddCameraFPS);
	REG(NewScene);
	REG(ClearScene);
   	REG(ActivateScene);
   	REG(CameraPos);
   	REG(GetCameraPos);
   	REG(MoveNode);
   	REG(RotateNode);
   	REG(GetPos);
   	REG(MakeNormalTexture);
   	REG(AddAnimator);
   	REG(AddLight);
}

#endif // HSCENE_H_INCLUDED
